package br.com.q7solucoes.commons.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import java.util.StringTokenizer;

/**
 * Classe que contem métodos para criação e manipulação de um arquivo de
 * configuracao.
 *
 * A marcacao de comentario usada e' o "#".
 *
 * @since 13 de maio de 2010
 * @author Elenilson Vieira - elenilson[at]elenilsonvieira.com
 * @see www.lavid.ufpb.br
 */
public class ConfigurationFile extends TextFile{
    
    /**
     * Tag de comentario. Deve ser colocada no inicio das linhas e o leitor
     * do arquivo a desprezara.
     */
    public static final String TAG_COMMENT_LINE = "#";

    /**
     * Regex que indica a separacao da tag e seu valor.
     *
     * Ex:
     * LABORATORIO = LAVID
     */
    public static final String TAG_VALUE_DELIMITER = "=";

    /**
     * Delimitador de um valor composto
     *
     * Ex:
     * FAMILIA = MAE $$ PAI $$ FILHOS
     */
    public static final String COMPOST_VALUE_DELIMITER = "&&";

    /**
     * Construtor da classe com um argumento. O arquivo sera criado no
     * diretorio corrente.
     *
     * @param name o nome do arquivo
     */
    public ConfigurationFile(String name) throws IOException {
        super(name);
    }

    public String getTagValue(String tag) throws FileNotFoundException, IOException{
        if(tag == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        Properties properties = new Properties();
        File file = new File(getPath());
        properties.load(new FileInputStream(file));

        String value = properties.getProperty(tag);

        if(value == null)
            value = getCommentTagValue(tag);

        return value;
    }

    public void setTagValue(String tag, String value) throws FileNotFoundException, IOException, IllegalAccessException{
        if(tag == null || value == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        tag = getUncommentTag(tag);

        if(containsTag(tag)){
            StringTokenizer content = new StringTokenizer(getContent(), "\n");
            StringBuilder newContent = new StringBuilder();

            while(content.hasMoreTokens()){
                String line = content.nextToken();

                if(line.startsWith(tag) || line.startsWith(getCommentTag(tag)))
                    line = String.format("%s=%s", tag, value);

                newContent.append(line).append("\n");
            }

            setContent(newContent.toString());
        }
    }

    public boolean removeTag(String tag) throws FileNotFoundException, IOException, IllegalAccessException{
        if(tag == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        if(containsTag(tag)){
            StringTokenizer content = new StringTokenizer(getContent(), "\n");
            StringBuilder newContent = new StringBuilder();

            while(content.hasMoreTokens()){
                String line = content.nextToken();

                if(!line.startsWith(tag) && !line.startsWith(getCommentTag(tag)))
                    newContent.append(line).append("\n");
            }

            setContent(newContent.toString());
            return true;
        }

        return false;
    }

    /**
     * Retorna a tag comentada.
     *
     * @param tag a tag a ser comentada
     *
     * @return a tag comentada
     */
    public static String getCommentTag(String tag){
        if(tag == null)
            return null;

        return !tag.startsWith(TAG_COMMENT_LINE) ?
            String.format("%s%s", TAG_COMMENT_LINE, tag)
            : tag;
    }

    /**
     * Retorna a tag descomentada.
     *
     * @param tag a tag a ser descomentada
     *
     * @return a tag descomentada
     */
    public static String getUncommentTag(String tag){
        if(tag == null || tag.length() < 2)
            return null;

        StringBuilder uncommentTag = new StringBuilder(tag);

        if(!tag.startsWith(TAG_COMMENT_LINE)){
            while(uncommentTag.substring(0, 1).equals(TAG_COMMENT_LINE))
                uncommentTag.delete(0, 1);
        }

        return uncommentTag.toString();
    }

    public boolean uncommentTag(String tag) throws FileNotFoundException, IOException, IllegalAccessException{
        if(tag == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        String uncomentTag = getUncommentTag(tag);
        if(!containsTag(tag) || containsExactTag(uncomentTag))
            return false;

        String tagValue = getTagValue(tag);
        setTagValue(uncomentTag, tagValue);

        return true;
    }

    public boolean containsTag(String tag) throws FileNotFoundException, IOException{
        if(tag == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        StringTokenizer content = new StringTokenizer(getContent(), "\n");

        while(content.hasMoreTokens()){
            String line = content.nextToken();

            if(line.startsWith(getUncommentTag(tag)) || line.startsWith(getCommentTag(tag)))
                return true;
        }

        return false;
    }

    public boolean commentTag(String tag) throws FileNotFoundException, IOException, IllegalAccessException{
        if(tag == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        if(!containsTag(tag) || containsTag(getCommentTag(tag)))
            return false;

        String tagValue = getTagValue(tag);
        removeTag(tag);

        setTagValue(getCommentTag(tag), tagValue);

        return true;
    }

    /**
     * Ler as propriedades do arquivo.
     *
     * @return Uma instancia de Properties com as propriedades encontradas no arquivo.
     */
    private Properties loadProperties() throws FileNotFoundException, IOException{
        Properties properties = new Properties();
        File file = new File(getPath());
        properties.load(new FileInputStream(file));

        return properties;
    }

    /**
     * Metodo auxiliar para procurar valores em tags comentadas, o que nao e'suportado pelo
     * metodo da interface. Esse metodo e'chamado apenas pelo metodo da interface caso
     * ele nao consiga recuperar o valor.
     *
     * @param tag a tag a ser procurada o valor
     *
     * @return Uma String com o valor da tag
     */
    private String getCommentTagValue(String tag) throws FileNotFoundException{
        if(tag == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        StringTokenizer content = new StringTokenizer(getContent(), "\n");
        String value = null;

        //Varre as linhas
        while(content.hasMoreTokens()){
            String token = content.nextToken();

            //Verifica se e'a linha procurada
            if(token.startsWith(getCommentTag(tag))){
                StringTokenizer line = new StringTokenizer(token, TAG_VALUE_DELIMITER);
                StringBuilder valueBuilder = new StringBuilder();

                //Elimina o primeiro token que e' a tag
                line.nextToken();

                while(line.hasMoreTokens())
                    valueBuilder.append(line.nextToken());

                value = valueBuilder.toString();
                break;
            }
        }

        return value;

    }

    public boolean containsExactTag(String tag) throws FileNotFoundException, IOException {
        if(tag == null)
            throw new IllegalArgumentException("Tag and value can't be null!");

        StringTokenizer content = new StringTokenizer(getContent(), "\n");

        while(content.hasMoreTokens()){
            String line = content.nextToken();

            if(line.startsWith(tag))
                return true;
        }

        return false;
    }

}
